package org.sdnstproject.interlayer.DummyProxyV13;

import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;

import org.sdnstproject.interlayer.interlayerProxy.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class OFFuzzerV13 {
	private static final Logger log = LoggerFactory.getLogger(OFFuzzerV13.class);
	private Configuration config = Configuration.getInstance();
	private AgentManager manager;
	public volatile boolean running = true;
	private ResultAnalyzer analyzer = ResultAnalyzer.getInstance();
	private DummySwitchV13 ofSwitch;
	private DummyControllerV13 ofController;
	private ListenV13 listenV13;
	private long start = 0;
	private long end = 0;
	
	public OFFuzzerV13() {
        this.manager = new AgentManager();
    }
	
	public String generateFlow(String proto) throws Exception{
        manager.hostmWriter(proto);
        String result = manager.hostmReader();
        if(result == null) log.error("generate flow and get result failed.");
        return result;
    }
	
	public void doMoreFuzzer() {
		String[] stras = new String[] {
//			"ofVersion:1.3,ofType:FEATURES_REPLY,childType:*,testWhich:controller,action:MODIFY,field:auxiliaryId,actionValue:32000",
			"ofVersion:1.3,ofType:FLOW_MOD,childType:*,testWhich:switch,action:MODIFY,field:hardTimeout,actionValue:2147483647"
		};
		FileWriter fileWriter = null;
		try {
			manager.start();
//			while(manager.readyAgent.get() < 2) {
//				Thread.sleep(500);
//			}
			synchronized (manager) {
				if(manager.readyAgent.get() < 2) {
					log.info("waitting for 2 agents start...");
					manager.wait();
				}
			}
			fileWriter = new FileWriter(config.getInterlayer_ROOT()+"/logs/fuzzresult.log",false);
			fileWriter.write("==================================================================="
					+ "\n#Fuzzing test result:\n");
			for(String stra:stras) {
				start = System.currentTimeMillis();
				String ret = doFuzzing(StrategesGen.getStrategyObject(stra));
				end = System.currentTimeMillis();
				log.info(ret);
				log.info(analyzer.toString());
				log.info("Running Time(s) : " + (end - start) / 1000.0);
				log.info("-----------------------------------------------------------------------------");
				fileWriter.write(ret+" ["+stra+"]\n");
				fileWriter.flush();
				if(ret == "FAIL") analyzer.printResultsToTerminnal(ret, stra);
			}
			//stop 2 agent in VM
			manager.getControllerm().doControl("stop agent");
			manager.getSwitchm().doControl("stop agent");
			Thread.sleep(500);
			manager.running = false;
			manager.interrupt();
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			try {
				fileWriter.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	public boolean doOpenFlowFuzzer() {
		String ret = null;
		FileWriter fileWriter =null;
		try {
			manager.start();
//			while(manager.readyAgent.get() < 2) {
//				Thread.sleep(500);
//			}
			synchronized (manager) {
				if(manager.readyAgent.get() < 2) {
					log.info("waitting for 2 agents start...");
					manager.wait();
				}
			}
			fileWriter = new FileWriter(config.getInterlayer_ROOT()+"/output/logs/fuzzresult.log",false);
			fileWriter.write("==================================================================="
					+ "\n#Fuzzing test result:\n");
			ArrayList<String> strategies = StrategesGen.readFromFile(config.getInterlayer_ROOT()+"/pyscript/strategiesV13.txt");
			for(String s:strategies) {
				Strategy strategy = StrategesGen.getStrategyObject(s);
				if(strategy == null) {
					log.info("get strategy failed. strategy: "+s);
					continue;
				}
				start = System.currentTimeMillis();
				ret = doFuzzing(strategy);
				end = System.currentTimeMillis();
				log.info(ret);
				log.info(analyzer.toString());
				log.info("Running Time(s) : " + (end - start) / 1000.0);
				log.info("-----------------------------------------------------------------------------");
				fileWriter.write(ret+" ["+s+"]\n");
				fileWriter.flush();
				if(ret == "FAIL") analyzer.printResultsToTerminnal(ret, s);
			}
			//stop 2 agent in VM
			manager.getControllerm().doControl("stop agent");
			manager.getSwitchm().doControl("stop agent");
			Thread.sleep(1000);
			manager.running = false;
			manager.interrupt();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				fileWriter.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return true;
	}
	
	public String doFuzzing(Strategy strategy) {
    	String ret = "UNKOWN";
    	try {
    		if(strategy.testWhich.equals("controller")) {
    			log.info("begin to fuzzing test controller, strategy = " + strategy.toString());
    			runController();
    			log.info("waitting controller start...");
    			String line = manager.getControllerm().getIn().readLine();
    			if(line.contains("start")) {
    				ofSwitch = new DummySwitchV13(strategy);
    				analyzer.init();
    				synchronized (ofSwitch) {
    					ofSwitch.start();
    					ofSwitch.wait(10000);//10sec
					}
    				if(ofSwitch.strategySend) {
						//result analysis
    					Thread.sleep(3000);//this line could be delete if added waiting response notify for other msg
    					analyzer.isSocketClose = ofSwitch.socket.isClosed();
    					ret = analyzer.getResult();
					}else {
						log.info("Test over 10 seconds, test timed out");
					}
    				ofSwitch.running = false;
    				ofSwitch.interrupt();
    				stopController();
    				Thread.sleep(500);
    			}else {
    				log.info("controller start failed.");
    			}
    		}else {//test switch
    			log.info("begin to fuzzing test switch, strategy = " + strategy.toString());
    			analyzer.init();
    			ofController = new DummyControllerV13(strategy);
    			listenV13 = new ListenV13(ofController);
    			manager.getSwitchm().doControl("run mininet");
//    			ofController.listeningSwitch();
				synchronized (ofController) {
					listenV13.start();
					ofController.wait(10000);//10sec
				}
				if(ofController.strategySend) {
					//result analysis
					Thread.sleep(3000);//this line could be delete if added waiting response notify for other msg
					analyzer.isSocketClose = ofController.targetSock.isClosed();
					ret = analyzer.getResult();
				}else {
					log.info("Test over 10 seconds, test timed out");
				}
				manager.getSwitchm().doControl("stop mininet");
				ofController.running = false;
				ofController.interrupt();
				listenV13.running = false;
				listenV13.interrupt();
				log.info("listen.isInterrupted:"+ listenV13.isInterrupted()
					+",ofController.isInterrupted:"+ofController.isInterrupted());
				log.info("listen.serverSock.isClosed:"+ listenV13.serverSock.isClosed()
					+",ofController.targetSock.isClosed:"+ofController.targetSock.isClosed());
				if(!listenV13.serverSock.isClosed()) listenV13.serverSock.close();
				if(!ofController.targetSock.isClosed()) ofController.targetSock.close();
				log.info("listen.serverSock.isClosed:"+ listenV13.serverSock.isClosed()
				+",ofController.targetSock.isClosed:"+ofController.targetSock.isClosed());
//				while(!listen.serverSock.isClosed()) {
//					listen.serverSock.close();
//					Thread.sleep(10);
//				}
//				while(!ofController.targetSock.isClosed()) {
//					ofController.targetSock.close();
//					Thread.sleep(10);
//				}
				Thread.sleep(500);
    		}
		} catch (Exception e) {
			manager.getSwitchm().doControl("stop mininet");
			stopController();
			e.printStackTrace();
		}
    	return ret;
    }
    
    public void runController() {
    	switch (config.getTargetController().toLowerCase()) {
    	case "ryu":
    		manager.getControllerm().doControl("run ryu");
    		break;
    	case "floodlight":
    		manager.getControllerm().doControl("run floodlight");
    		break;
    	case "odl":
    		manager.getControllerm().doControl("run odl");
    		break;
    	case "onos":
    		manager.getControllerm().doControl("run onos");
    		break;
    	case "pox":
    		manager.getControllerm().doControl("run pox");
    		break;
    	}
    }
    
    public void stopController() {
    	switch (config.getTargetController().toLowerCase()) {
			case "ryu":
				manager.getControllerm().doControl("stop ryu");
				break;
			case "floodlight":
				manager.getControllerm().doControl("stop floodlight");
				break;
			case "odl":
				manager.getControllerm().doControl("stop odl");
				break;
			case "onos":
				manager.getControllerm().doControl("stop onos");
				break;
			case "pox":
				manager.getControllerm().doControl("stop pox");
				break;
		}
    }
	
}
